
#include "p2d_force_sphereex.h"

#include "script/scripthelper.h"
#include "util/log.h"		    // log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    // Needs to come in early

#include "math/mat4x4.h"

#include "dd/dd_man.h"

#include <cassert>
using namespace part2d;


P2DForceSphereEx::P2DForceSphereEx()
{
    m_Offset    = math::Vec2();
    m_Magnitude = 0.0f;
    m_MagSqr    = 0.0f;       // Magnitude squared precalced
    m_BounceForce = 0.0f;
    m_Action    = Bounce;
}

P2DForceSphereEx::~P2DForceSphereEx()
{

}
P2DForce*	P2DForceSphereEx::Clone()
{
	P2DForceSphereEx*	pForce = new P2DForceSphereEx();
	pForce->m_Offset		= m_Offset;
	pForce->m_BounceForce	= m_BounceForce;
	pForce->m_Magnitude		= m_Magnitude;
	pForce->m_Action		= m_Action;
	pForce->m_MagSqr		= m_MagSqr;
	return pForce;
}

bool    P2DForceSphereEx::Load(LuaPlus::LuaObject& _LObj)
{
    m_Offset        = script::ScriptHelper::GetTableVec2(_LObj, "Offset", math::Vec2(0, 0));
    m_Magnitude     = script::ScriptHelper::GetTableFloat(_LObj, "Magnitude", 0.1f);
    m_BounceForce   = script::ScriptHelper::GetTableFloat(_LObj, "BounceForce", 0.9f);
    m_Action        = GetAction(script::ScriptHelper::GetTableString(_LObj, "Action", "Move"));
    m_MagSqr = m_Magnitude * m_Magnitude;
     return true;
}

bool	P2DForceSphereEx::RenderDebug(const math::Vec2& Pos)
{
	dd::Manager::Instance().AddCircle2D(Pos + m_Offset, m_Magnitude, ColorRGBA(80, 255, 0, 0));
    return true;
}

bool	P2DForceSphereEx::MoveParticles(const math::Vec2& GroupCenter, Particle2D* _Particles, unsigned int _usCount, float dt)
{
    switch (m_Action)
    {
        case Move:      return MovePush(GroupCenter, _Particles, _usCount, dt);
        case Bounce:    return MoveBounce(GroupCenter, _Particles, _usCount, dt);
        case Kill:      return MoveKill(GroupCenter, _Particles, _usCount, dt);
    }
    return true;
}

bool P2DForceSphereEx::MovePush(const math::Vec2& GroupCenter, Particle2D* _Particles, unsigned int _usCount, float dt)
{
    math::Vec2      Pos = GroupCenter + m_Offset;

    for (unsigned int i=0; i<_usCount; i++)
    {
        Particle2D* pPart = &_Particles[i];
        if (pPart->Active)
        {
            math::Vec2  Delta    = Pos - pPart->Pos;
            float	    DistSq  = Delta.MagSqr();

            // Containment is within 
			if (DistSq < m_MagSqr)
            {
                    // Push to bounds of sphere
                    Delta.Normalize();
					pPart->Pos = Pos - (Delta * m_Magnitude);
            }
        }
    }
    return true;
}

bool P2DForceSphereEx::MoveBounce(const math::Vec2& GroupCenter,Particle2D* _Particles, unsigned int _usCount, float dt)
{
    math::Vec2      Pos = GroupCenter + m_Offset;

    for (unsigned int i=0; i<_usCount; i++)
    {
        Particle2D* pPart = &_Particles[i];
        if (pPart->Active)
        {
            math::Vec2  Delta    = Pos - pPart->Pos;
            float	    DistSq  = Delta.MagSqr();

            // Containment is within 
			if (DistSq < m_MagSqr)
            {
                // Push to bounds of sphere
                math::Vec2  NormDelta = Delta;
                NormDelta.Normalize();
				pPart->Pos= Pos - (NormDelta * m_Magnitude);

                // Then add a force determined by how fast it was going
                //		Angle of reflection
                //		 2 * (Normal dot vDir) * Normal - vDir
				float SpeedMag = pPart->Speed;
                float InvSpeed = 1.0f / SpeedMag;
				float DotProd  = 2.0f * ((pPart->Dir)^NormDelta);
				pPart->Dir  = -(pPart->Dir) + (NormDelta * DotProd);
				float NewSpeed = pPart->Dir.Magnitude();
				pPart->Dir.Normalize();
				pPart->Speed  =-SpeedMag*m_BounceForce*NewSpeed;  // Bounce factor
            }
        }
    }
    return true;
}

bool P2DForceSphereEx::MoveKill(const math::Vec2& GroupCenter,Particle2D* _Particles, unsigned int _usCount, float dt)
{
    math::Vec2      Pos = GroupCenter + m_Offset;

    for (unsigned int i=0; i<_usCount; i++)
    {
        Particle2D* pPart = &_Particles[i];
        if (pPart->Active)
        {
            math::Vec2  Delta    = Pos - pPart->Pos;
            float	    DistSq  = Delta.MagSqr();

            // Containment is within 
			if (DistSq < m_MagSqr)
            {
                pPart->Kill = true;
            }
        }
    }
    return true;
}

